#ifndef SKYROAD_BIN_OARCHIVE_H_
#define SKYROAD_BIN_OARCHIVE_H_

#include <string>
#include <vector>
#include <boost/array.hpp>
#include <skyroad/archive/nvp.hpp>
#include <skyroad/detail/config.hpp>

class SKYROAD_DECL bin_oarchive
{
public:
    unsigned char* begin()
    {
       return m_first;
    }

    unsigned char* end()
    {
        return m_last;
    }

    bin_oarchive(unsigned char* first, unsigned char* last, int version)
    :m_data(first), m_first(first), m_last(last), m_version(version)
    {
    }

    bin_oarchive(unsigned char* first, std::size_t length, int version)
    :m_data(first), m_first(first), m_last(first+length), m_version(version)
    {
    }

    void seek(unsigned char* pos) 
    {
        m_first = pos;
    }

    void write_float(float& f)
    {
        write(f);
    }

    void write_string(const std::string& s)
    {
        write(s);
    }

    void write_wstring(const std::wstring& s) 
    {
        write(s);
    }


    void write_char(unsigned char c) 
    {
        write(c);
    }

    void write_ushort(unsigned short s)
    {
        write(s);
    }

    void write_ulong(unsigned long l) 
    {
        write(l);
    }

    void write_int(int l) 
    {
        write(l);
    }

    void write_uint64(unsigned __int64 v) 
    {
        write(v);
    }

    template <typename T>
    void write_vector(std::vector<T>& v)
    {
        write(v);
    }

    template <typename T, std::size_t N>
    void write_array(boost::array<T, N>& a) 
    {
        write(a);
    }

    void write(const std::string& s)
    {
        m_first = std::copy(s.c_str(), s.c_str()+s.size()+1, m_first);
    }

    void write(const std::wstring& s)
    {
        m_first = std::copy((const unsigned char*)s.c_str(), (const unsigned char*)s.c_str()+(s.size()+1)*2, m_first);
    }


    void write(unsigned char c) 
    {
        m_first = std::copy((const unsigned char*)&c, (const unsigned char*)&c+1, m_first);
    }

    void write(unsigned short s)
    {
        m_first = std::copy((const unsigned char*)&s, (const unsigned char*)&s+sizeof(s), m_first);
    }

    void write(unsigned long l) 
    {
        m_first = std::copy((const unsigned char*)&l, (const unsigned char*)&l+sizeof(l), m_first);
    }

    void write(int i)
    {
        m_first = std::copy((const unsigned char*)&i, (const unsigned char*)&i+sizeof(i), m_first);
    }

    void write(float f)
    {
        m_first = std::copy((const unsigned char*)&f, (const unsigned char*)&f+sizeof(f), m_first);
    }


    void write(unsigned __int64 v)
    {
        m_first = std::copy((const unsigned char*)&v, (const unsigned char*)&v+sizeof(v), m_first);
    }

    template <typename T>
    void write(std::vector<T>& v) 
    {
        write((unsigned short)v.size());
         for (int i = 0; i < v.size(); i++)
         {
            serialize(*this, v[i], m_version);
         }
   }

    template <typename T, std::size_t N>
    void write(boost::array<T, N>& a)
    {
        m_first = std::copy((const unsigned char*)&a[0], (const unsigned char*)&a[0]+sizeof(T)*a.size(), m_first);
    }

private:
    unsigned char* m_data; 
    unsigned char* m_first;
    unsigned char* m_last;
    int m_version;
};


template <class T>
bin_oarchive& operator &(bin_oarchive& out, std::vector<T>& item)
{
    out.write(item);
    return out;
}

template <typename T, std::size_t N>
bin_oarchive& operator &(bin_oarchive& out, boost::array<T,N>& value)
{
    out.write(value);
    return out;
}

inline bin_oarchive& operator &(bin_oarchive& out, float& value)
{
    out.write(value);
    return out;
}

inline bin_oarchive& operator &(bin_oarchive& out, std::string& value)
{
    out.write(value);
    return out;
}

inline bin_oarchive& operator &(bin_oarchive& out, std::wstring& value) 
{
    out.write(value);
    return out;
}


inline bin_oarchive& operator &(bin_oarchive& out, unsigned char value)
{
    out.write(value);
    return out;
}

inline bin_oarchive& operator &(bin_oarchive& out, unsigned short value)
{
    out.write(value);
    return out;
}

inline bin_oarchive& operator &(bin_oarchive& out, unsigned long value)
{
    out.write(value);
    return out;
}

inline bin_oarchive& operator &(bin_oarchive& out, int value)
{
    out.write(value);
    return out;
}

inline bin_oarchive& operator &(bin_oarchive& out, unsigned __int64 value)
{
    out.write(value);
    return out;
}

template <typename Value>
inline bin_oarchive& operator &(bin_oarchive& out, nvp<Value> nv)
{
    out.write(*nv.second);
    return out;
}
#endif

